package com.uuah.server;

import java.io.Serializable;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.uuah.config.i18n.Message;
import com.uuah.exception.UuahException;
import com.uuah.server.api.IUuahContext;
import com.uuah.server.api.config.IConfigurationBuilder;
import com.uuah.server.config.i18n.ServerMessages;
import com.uuah.server.context.UuahContextFactory;
import com.uuah.server.context.UuahEventLinstenerBuilder;
import com.uuah.server.exception.config.ConfigurationException;
import com.uuah.server.management.stats.UuahServerStats;
import com.uuah.utils.ClassUtils;
import com.uuah.utils.IOUtils;
import com.uuah.utils.StringUtils;
import com.uuah.utils.SystemUtils;
import com.uuah.utils.UuahUrlStreamHandlerFactory;

public class UuahServer implements Runnable {
	private static final Logger logger = LoggerFactory
			.getLogger(UuahServer.class);
	public static final String CLI_OPTIONS[][] = {
			{ "config", "true", "Config File" },
			{ "main", "true", "Main Class" } };

	protected static final String CLASSNAME_SPRING_CONFIG_BUILDER = "com.uuah.server.config.spring.SpringXmlConfigurationBuilder";

	public static final String DEFAULT_CONFIGURATION = "appServer-config.xml";

	private String configurationResources = null;

	private static String configBuilderClassName = null;

	protected Map<String, String> options = Collections.emptyMap();

	protected static IUuahContext uuahContext = null;
	private static long startTime;

	public static long getStartTime() {
		return startTime;
	}

	private static UuahServerStats serverStats;

	public static UuahServerStats getServerStats() {
		return serverStats;
	}

	public static void main(String[] args) throws Exception {

		UuahServer server = new UuahServer(args);
		server.start(false);
	}

	public UuahServer() {
		init(new String[] {});
	}

	public UuahServer(String configResources) {
		init(new String[] { "-config", configResources });
	}

	public UuahServer(String[] args) throws IllegalArgumentException {
		init(args);
	}

	protected void init(String[] args) throws IllegalArgumentException {
		Map<String, String> options;
		try {
			options = SystemUtils.getCommandLineOptions(args, CLI_OPTIONS);
		} catch (UuahException ex) {
			throw new IllegalArgumentException(ex.toString());
		}

		UuahUrlStreamHandlerFactory.installUrlStreamHandlerFactory();

		String config = (String) options.get("config");
		if (config == null) {
			logger.warn(": " + DEFAULT_CONFIGURATION);
			URL configUrl = IOUtils.getResourceAsUrl(DEFAULT_CONFIGURATION,
					UuahServer.class, true, false);
			if (configUrl != null) {
				config = configUrl.toExternalForm();
			} else {
				System.out.println(ServerMessages.configNotFoundUsage());
				System.exit(-1);
			}
		}

		if (config != null) {
			setConfigurationResource(config);
		}
		try {
			setConfigBuilderClassName(CLASSNAME_SPRING_CONFIG_BUILDER);
		} catch (ClassNotFoundException e) {
			logger.error(e.getMessage());
		}
	}

	public void start(boolean ownThread) {
		if (ownThread) {
			Thread serverThread = new Thread(this, "UuahAppServer");
			serverThread.setDaemon(true);
			serverThread.start();
		} else {
			run();
		}
	}

	public void run() {
		startTime = System.currentTimeMillis();
		serverStats = new UuahServerStats();
		serverStats.setStartTime(startTime);
		try {
			logger.info("Uuah App Server initing ...");
			initialize();
			UuahEventLinstenerBuilder.registerEventListener();
			logger.info("Uuah App Server starting...");
			uuahContext.start();
			serverStats.setRunStatus(true);
		} catch (Throwable e) {
			shutdown(e);
		}
	}

	public static void setConfigBuilderClassName(String builderClassName)
			throws ClassNotFoundException {
		if (builderClassName != null) {
			Class cls = ClassUtils
					.loadClass(builderClassName, UuahServer.class);
			if (IConfigurationBuilder.class.isAssignableFrom(cls)) {
				UuahServer.configBuilderClassName = builderClassName;
			} else {
				throw new IllegalArgumentException(
						"Not a usable ConfigurationBuilder class: "
								+ builderClassName);
			}
		} else {
			UuahServer.configBuilderClassName = null;
		}
	}

	public static String getConfigBuilderClassName() {
		if (StringUtils.isNotBlank(configBuilderClassName)) {
			return configBuilderClassName;
		} else {
			return CLASSNAME_SPRING_CONFIG_BUILDER;
		}

	}

	public void initialize() throws Exception {
		Runtime.getRuntime().addShutdownHook(new ShutdownThread());

		if (configurationResources == null) {
			logger.warn(": " + DEFAULT_CONFIGURATION);
			configurationResources = DEFAULT_CONFIGURATION;
		}

		IConfigurationBuilder cfgBuilder;

		try {
			cfgBuilder = (IConfigurationBuilder) ClassUtils.instanciateClass(
					getConfigBuilderClassName(),
					new Object[] { configurationResources }, UuahServer.class);
		} catch (Exception e) {
			throw new ConfigurationException(ServerMessages
					.failedToLoad(getConfigBuilderClassName()), e);
		}

		if (!cfgBuilder.isConfigured()) {
			UuahContextFactory uuahContextFactory = new UuahContextFactory();
			uuahContext = uuahContextFactory.createUuahContext(cfgBuilder);
		}
	}

	public void shutdown(Throwable e) {
		logger.info("AppServer shutdown...");
		Message msg = ServerMessages.fatalErrorWhileRunning();
		List<Serializable> msgs = new ArrayList<Serializable>();
		msgs.add(msg.getMessage());
		msgs.add(e.getMessage() + " (" + e.getClass().getName() + ")");
		msgs.add(" ");
		msgs.add(ServerMessages.fatalErrorInShutdown());
		if (uuahContext != null) {
			msgs
					.add(ServerMessages.serverStartedAt(uuahContext
							.getStartDate()));
		}
		msgs.add(ServerMessages.serverShutdownAt(new Date()));

		String shutdownMessage = StringUtils.getBoilerPlate(msgs, '*', 140);
		logger.warn(shutdownMessage);

		if (uuahContext != null) {
			uuahContext.dispose();
		}
		System.exit(0);
	}

	public void shutdown() {
		logger.info("AppServer shutdonw...");

		uuahContext.dispose();
		System.exit(0);
	}

	public String getConfigurationResource() {
		return configurationResources;
	}

	public void setConfigurationResource(String configurationResources) {
		this.configurationResources = configurationResources;
	}

	public static IUuahContext getUuahContext() {
		return uuahContext;
	}

	public static void setUuahContext(IUuahContext uuahContext) {
		UuahServer.uuahContext = uuahContext;
	}

	private static class ShutdownThread extends Thread {
		public void run() {
			if (uuahContext != null && !uuahContext.isDisposed()
					&& !uuahContext.isDisposing()) {
				uuahContext.dispose();
			}
			UuahServer.getServerStats().setRunStatus(false);
		}
	}
}
